/*********************************************************
 *
 *  Ports all the way down - patwd
 *  ------------------------------
 *
 *  os.c
 *
 *  Copyright (C) 2012 by Sebastian Roschke (bykhee@gmail.com)
 *
 *  OS related funtionality, i.e. updating list of listening ports.
 *
 *  Distributed under the terms and conditions of GNU LGPL.
 *
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "os.h"
#include "debug.h"
#include "alloc-inl.h"

extern u8 ports[NUM_PORTS];
extern u16 p_cnt;

u8 tmp_ports[NUM_PORTS];

/*****************************************
 *
 * This function is borrowed from netstat.
 * Thx.
 *
 *****************************************/
void parse_line(int lnr, const u8 *line) {
  
  u64 rxq, txq, time_len, retr, inode;
  int num, local_port, rem_port, d, state, uid, timer_run, timeout;
  char rem_addr[128], local_addr[128], more[512];
#if HAVE_AFINET6
  struct sockaddr_in6 localaddr, remaddr;
  char addr6[INET6_ADDRSTRLEN];
  struct in6_addr in6;
  extern struct aftype inet6_aftype;
#else
  struct sockaddr_in localaddr, remaddr;
#endif

  if (lnr == 0)
    return;

  num = sscanf((char*)line,
    "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %lu %512s\n",
    &d, local_addr, &local_port, rem_addr, &rem_port, &state,
    &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);

  if (strlen(local_addr) > 8) {
#if HAVE_AFINET6
    /* Demangle what the kernel gives us */
    sscanf(local_addr, "%08X%08X%08X%08X",
      &in6.s6_addr32[0], &in6.s6_addr32[1],
      &in6.s6_addr32[2], &in6.s6_addr32[3]);
    inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
    inet6_aftype.input(1, addr6, (struct sockaddr *) &localaddr);
    sscanf(rem_addr, "%08X%08X%08X%08X",
      &in6.s6_addr32[0], &in6.s6_addr32[1],
      &in6.s6_addr32[2], &in6.s6_addr32[3]);
    inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
    inet6_aftype.input(1, addr6, (struct sockaddr *) &remaddr);
    localaddr.sin6_family = AF_INET6;
    remaddr.sin6_family = AF_INET6;
#endif
  } else {
    sscanf(local_addr, "%X",
      &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
    sscanf(rem_addr, "%X",
      &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
    ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
    ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
  }

  if (num < 11) {
    log_error((u8*)"warning, got bogus tcp line.");
    printf("lnr: %d num: %d\n%s", lnr, num, line);
    return;
  }

  if (state == 0x0A) {
    u8* lstr = alloc_printf("Open port found: %d", local_port);
    log_debug(lstr);
    ck_free(lstr);
    
    // Set the port table
    tmp_ports[local_port] = 1;
  }
}

void read_file(const u8* fname) {

  u16 i = 0;
  for (i = 0; i < NUM_PORTS; i++) {
    tmp_ports[i] = 0;
  }

  FILE *file = fopen((char*)fname, "r");
  if ( file != NULL ) {
    u8 line[256];
 
    u32 cnt = 0;
    while (fgets((char*)line, sizeof(line), file) != NULL) {
      u32 ncnt = cnt + 1;
      if (ncnt < cnt) {
        log_error((u8*)"Whoops, hit by an integer overflow!");
	exit(2);
      }
      parse_line(cnt++, line);
    }
    fclose(file);
  } else {
    perror((char*)fname);
    exit(2);
  }

  // Copy updated tmp_ports to ports array
  u16 tmp_p_cnt = 0;
  for (i = 0; i < NUM_PORTS; i++) {
    ports[i] = !tmp_ports[i];
    if (ports[i]) tmp_p_cnt++;
  }
  p_cnt = tmp_p_cnt;
}

void *update_ports(void* char_array) {

  u8** proc_files = (u8**) char_array;

  while (1) {
    read_file(proc_files[0]);
    read_file(proc_files[1]);
    sleep(2);
  }
}
